/*
 *
 *  *    Copyright 2020-2021 Luter.me
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *      http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.luter.heimdall.core.authorization.provider;

import com.luter.heimdall.core.authorization.authority.GrantedAuthority;
import com.luter.heimdall.core.authorization.authority.SimpleGrantedAuthority;
import com.luter.heimdall.core.authorization.provider.model.AppDO;
import com.luter.heimdall.core.authorization.provider.model.AuthorityDO;
import com.luter.heimdall.core.authorization.provider.model.UserDO;
import com.luter.heimdall.core.config.ConfigManager;
import com.luter.heimdall.core.exception.HeimdallException;
import com.luter.heimdall.core.utils.PathUtil;
import com.luter.heimdall.core.utils.StrUtils;
import org.slf4j.Logger;
import org.yaml.snakeyaml.Yaml;

import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.*;
import java.util.stream.Collectors;

import static com.luter.heimdall.core.utils.StrUtils.count;
import static org.slf4j.LoggerFactory.getLogger;

/**
 * 解析权限配置参数
 *
 * @author luter
 */
public class DocumentAuthorizationParser {
    /**
     * The constant log.
     */
    private static final transient Logger log = getLogger(DocumentAuthorizationParser.class);
    /**
     * The constant CONFIG_FILE_NAME.
     */
    private static final String CONFIG_FILE_NAME = "heimdall-authority";

    /**
     * Parse authority do.
     *
     * @return the authority do
     */
    private static AuthorityDO parse() {
        Yaml yaml = new Yaml();
        InputStream inputStream = null;
        try {
            inputStream = ConfigManager.class.getClassLoader().getResourceAsStream(CONFIG_FILE_NAME + ".yaml");
            if (null == inputStream) {
                inputStream = ConfigManager.class.getClassLoader().getResourceAsStream(CONFIG_FILE_NAME + ".yml");
                if (null == inputStream) {
                    String error = "Heimdall authority properties initialized failed.The configuration file  was not read correctly. " +
                            "Please Make sure that the correct configuration file " +
                            "named:[authority.yaml or authority.yml] exists in the classpath";
                    log.warn("[DocumentAuthorizationParser->parse]::{}", error);
                    throw new HeimdallException(error);
                }
            }
            return yaml.loadAs(inputStream, AuthorityDO.class);
        } finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * Parse users list.
     *
     * @return the list
     */
    public static List<UserDO> parseUsers() {
        return parse().getUsers();
    }

    /**
     * Generate config.
     *
     * @throws IOException the io exception
     */
    private static void generateConfig() throws IOException {
        AuthorityDO authorityDO = new AuthorityDO();
        Yaml yaml = new Yaml();
        Writer writer = new FileWriter(CONFIG_FILE_NAME);
        yaml.dump(authorityDO, writer);
    }

    /**
     * 解析 用户权限 配置
     *
     * @param appId the app id
     * @param uid   the uid
     * @return the collection
     */
    public static List<? extends GrantedAuthority> parseUserPerms(String appId, String uid) {
        AuthorityDO users = parse();
        if (null != users.getUsers() && !users.getUsers().isEmpty()) {
            final Optional<UserDO> first = users.getUsers().stream()
                    .filter(u -> u.getAppId().equals(appId) && u.getUserId().equals(uid)).findFirst();
            if (first.isPresent()) {
                final UserDO userVO = first.get();
                final List<String> perms = userVO.getPerms();
                if (null != perms && !perms.isEmpty()) {
                    return perms.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
                }
            }
        }
        return new ArrayList<>();
    }

    /**
     * 解析 应用权限 配置
     *
     * @param appId the app id
     * @return the map
     */
    public static Map<String, List<String>> parseAppPerms(String appId) {
        AuthorityDO authorityDO = parse();
        final List<AppDO> auths = authorityDO.getAuthorities();
        final Optional<AppDO> first = auths.stream().filter(d -> d.getAppId().equals(appId)).findFirst();
        if (first.isPresent()) {
            final AppDO appVo = first.get();
            Map<String, List<String>> appPerms = new LinkedHashMap<>();
            appVo.getAuths().forEach(d -> {
                if (count(d, '=') != 2) {
                    log.warn("Incorrect configuration parameters,Only two equal signs  are allowed.eg: /cat==admin,user");
                    throw new IllegalArgumentException("Incorrect configuration parameters,Only two equal signs  are allowed.eg: /cat==admin,user" + d);
                } else {
                    final String[] split = d.split("==");
                    String url = split[0];
                    String attrs = split[1];
                    if (PathUtil.isValidPath(url) && StrUtils.isNotBlank(attrs)) {
                        appPerms.put(url, Arrays.asList(attrs.split(",")));
                    }
                }
            });
            return appPerms;
        }
        log.warn("No app authorities found with id:{}", appId);
        return new HashMap<>();

    }
}
